package mcommand;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;

import javax.swing.ImageIcon;

/**
 * Handles how the missile and explosion should be animated on the board.
 *
 * @author Marcel, Christian, Klaus
 */
public class Missile {
    // defines where to find the cross image
    private static final String TARGETCROSSPATH = "assets/cross.png";
    
    // defines where to find the gun sound
    public static String PATH_SOUND_EXPLOSION = "assets/gun.wav";
    private final SoundHandler soundExplosion;
    
    // initiates the variable for the cross image
    protected static Image targetCross;

    // initiates the variable for the missile state
    protected MissileState ms;
    
    // initiates the variables for the start positions
    protected int start_x;
    protected int start_y;

    // defines the move progresses with a standard value
    protected float move_progress = 0.0f;
    private float explode_progress = 0.0f;
    
    // defines the standard explosion size
    protected int explode_size = 50;

    // used to calculate the speed
    protected float std_move_speed = 0.2f;
    
    // constant for calculating move speed
    protected float move_constant = 1000.0f;

    // move speed in percent per tick
    protected float move_speed = 0.01f;

    // exlpsoion speed in percent per tick
    private float explode_speed = 0.05f;

    // initiates the variables for the positions of the target
    protected int target_x;
    protected int target_y;

    /**
     * Sets the missile state to FLYING and sets standard values for the start
     * position tries to load the target cross image.
     */
    public Missile() {
        // sets up a new SoundHandler for the explosion sound of the missile
        soundExplosion = new SoundHandler(PATH_SOUND_EXPLOSION);
        
        // if the variable targetCross holds no value
        if (targetCross == null) {
            
            // tries to load into the variable targetCross the cross image
            try {
                ImageIcon ii = new ImageIcon(this.getClass().getResource(
                        TARGETCROSSPATH));
                targetCross = ii.getImage();
                
                // if something went wrong a message is printed out on the
                // console
            } catch (Exception e) {
                System.err.println("Target Cross konnte nicht geladen werden!");
            }
        }
        
        // sets standard values for the start positions
        start_x = 0;
        start_y = 0;
        
        // sets the missile state to FLYING
        ms = MissileState.FLYING;
    }

    /**
     * Sets the target position.
     * 
     * @param x x-position
     * @param y y-position
     */
    public final void setTarget(final int x, final int y) {
        target_x = x;
        target_y = y;
        // calls the calculateMoveSpeed method to calculate the move speed
        calculateMoveSpeed();
    }

    /**
     * Returns the current x position of the target.
     * 
     * @return x
     */
    public final int getTargetX() {
        return target_x;
    }

    /**
     * Returns the current y position of the target.
     * 
     * @return y
     */
    public final int getTargetY() {
        return target_y;
    }

    /**
     * Sets the start position.
     * 
     * @param x start-x position
     * @param y start-y position
     */
    public final void setStart(final int x, final int y) {
        start_x = x;
        start_y = y;

        // calls the calculateMoveSpeed method to calculate the move speed
        calculateMoveSpeed();
    }

    /**
     * Calculates how long the missile should fly to the target(in seconds) if
     * the way is longer the missile flies longer.
     */
    protected final void calculateMoveSpeed() {
        // calculates the seconds how long the missile should fly to the target
        float secondsToTarget = (getDistanceToTarget() / std_move_speed);
        // defines the move_speed variable with the secondsToTarget value
        move_speed = ((move_constant / Board.DELAY) / secondsToTarget);
    }

    /**
     * Sets the move speed.
     * 
     * @param speed speed
     */
    public final void setMoveSpeed(final float speed) {
        this.std_move_speed = speed;
        // calculates the move speed again for the speed value
        calculateMoveSpeed();
    }

    /**
     * Sets how big the moveprogress should be.
     * 
     * @param progress progress
     */
    public final void setMoveProgress(final float progress) {
        this.move_progress = progress;
    }

    /**
     * Sets the missile state.
     * 
     * @param ms ms
     */
    public final void setState(final MissileState ms) {
        this.ms = ms;
    }

    /**
     * Handles how the missile and explosion animation should be updated
     * (animated).
     */
    public final void update() {
        // handles moving
        if (ms == MissileState.FLYING) {
            // if target is not reached
            if (move_progress < 1.0f) {
                move_progress += move_speed;
                // if target is reached
            } else {
                // plays explosion sound
                soundExplosion.playSoundOnce();
                // changes the missile state to EXPLODING_OUT
                ms = MissileState.EXPLODING_OUT;
            }
        }

        // handles the explosion
        if (ms == MissileState.EXPLODING_OUT) {
            // if explosion max is not reached yet
            if (explode_progress < 1.0f) {
                explode_progress += explode_speed;
                // if explosion max is reached
            } else {
                ms = MissileState.EXPLODING_IN;
            }
            // if the missile state is EXPLODING_IN, explosion gets smaller
        } else if (ms == MissileState.EXPLODING_IN) {
            // if the explosion progress is bigger than the minimum of explosion
            // can be
            if (explode_progress > 0.0f) {
                explode_progress -= explode_speed;
                // if min size of explosion is reached
            } else {
                // sets the missile state to DESTROYED
                ms = MissileState.DESTROYED;
            }
        }
    }

    /**
     * Returns which x position the rocket head has currently.
     * 
     * @return x of rocket head
     */
    public final int getX() {
        if (move_progress > 1.0f) {
            move_progress = 1.0f;
        }

        return (int) (start_x + ((target_x - start_x) * move_progress));
    }

    /**
     * Returns which y position the rocket head has currently.
     * 
     * @return y of rocket head
     */
    public final int getY() {
        if (move_progress > 1.0f) {
            move_progress = 1.0f;
        }

        return (int) (start_y + ((target_y - start_y) * move_progress));
    }

    /**
     * Calculates the distance to the target.
     * 
     * @return int distance (start->target)
     */
    protected final int getDistanceToTarget() {
        double dx = target_x - start_x;
        double dy = target_y - start_y;
        // calculates with pythagoras
        return (int) Math.sqrt((dx * dx) + (dy * dy));
    }

    /**
     * checks if missile is destroyed and returns true if it is destroyed either
     * false.
     * 
     * @return true or false
     */
    public final boolean isDestroyed() {
        if (ms == MissileState.DESTROYED) {
            return true;
        }

        return false;
    }

    /**
     * empty paint method (does currently nothing).
     * 
     * @param g2d g2d
     */
    public void paint(final Graphics2D g2d) {

    }

    /**
     * returns the current explosion size.
     * 
     * @return int explosion size
     */
    public final int getExplosionSize() {
        return (int) (explode_size * explode_progress);
    }

    /**
     * if the missile is currently exploding it returns true either false.
     * 
     * @return true or false
     */
    public final boolean isExploding() {
        if (ms == MissileState.EXPLODING_IN
                || ms == MissileState.EXPLODING_OUT) {
            return true;
        }

        return false;
    }

    /**
     * paints the explosion ellipse.
     * 
     * @return new ellipse
     */
    public final Shape getExplosionShape() {
        return new Ellipse2D.Float((getX() - (getExplosionSize() / 2)),
                (getY() - (getExplosionSize() / 2)), getExplosionSize(),
                getExplosionSize());
    }

    /**
     * hold the missile states (FLYING, EXPLODING_OUT, EXPLODING_IN, DESTROYED).
     */
    public enum MissileState {
        FLYING,
        EXPLODING_OUT,
        EXPLODING_IN,
        DESTROYED
    }

}
